/*
 * Copyright 2008 Jens Meiss
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package hmvc4gwt.client.component;

import hmvc4gwt.client.controller.IController;
import hmvc4gwt.client.event.IEvent;
import hmvc4gwt.client.event.IEventListener;
import hmvc4gwt.client.model.IModel;
import hmvc4gwt.client.view.IView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The AComponent is an Implementation of the IComponent-Interface. You can use
 * this Class to build you own IComponent-Implementation.
 * 
 * @author jens.meiss
 * 
 */
public abstract class AComponent implements IComponent {

	/**
	 * contains the Controller of the Component
	 */
	private IController controller;

	/**
	 * contains all Terminated Child Components
	 */
	private List listTerminatedChilds = new ArrayList(4);

	/**
	 * contains all Listeners of the Component, <String>,<List> TODO HashMap
	 * initialSize setzen
	 */
	private Map mapEventListener = new HashMap(5);

	/**
	 * contains the Model of the Component
	 */
	private IModel model;

	/**
	 * contains the parent-Component
	 */
	private IComponent parentComponent;

	/**
	 * was IComponent.terminate() called
	 */
	private boolean terminated = false;

	/**
	 * contains the View of the Component
	 */
	private IView view;

	/**
	 * adds an Child-Component
	 * 
	 * @param component
	 * @return
	 */
	protected abstract IComponent addChildComponent(IComponent component);

	public void addTerminatedChildComponent(IComponent component) {
		listTerminatedChilds.add(component);
	}

	/**
	 * fires the Event to all Listeners
	 * 
	 * @param event
	 */
	protected abstract void callLocalListeners(IEvent event);

	/**
	 * connectes all component part to each other
	 */
	private void connectComponentParts() {
		/*
		 * connect Controller
		 */
		controller.setComponent(this);

		/*
		 * connect the Model
		 */
		if (model != null) {
			model.setController(controller);
		}

		/*
		 * connect the View
		 */
		if (view != null) {
			view.setController(controller);
		}
	}

	/**
	 * creates an Controller-Child-Component
	 * @param parentComponent
	 * @param controller
	 */
	protected void createComponent(IComponent parentComponent,
			IController controller) {

		setParentComponent(parentComponent);
		setController(controller);

		init();
	}

	/**
	 * creates an Controller-Model-Child-Component
	 * @param parentComponent
	 * @param controller
	 * @param model
	 */
	protected void createComponent(IComponent parentComponent,
			IController controller, IModel model) {

		setParentComponent(parentComponent);
		setController(controller);
		setModel(model);

		init();
	}

	/**
	 * creates an Controller-Model-View-Child-Component
	 * @param parentComponent
	 * @param controller
	 * @param model
	 * @param view
	 */
	protected void createComponent(IComponent parentComponent,
			IController controller, IModel model, IView view) {

		setParentComponent(parentComponent);
		setController(controller);
		setModel(model);
		setView(view);

		init();
	}

	/**
	 * creates an Controller-View-Child-Component
	 * @param parentComponent
	 * @param controller
	 * @param view
	 */
	protected void createComponent(IComponent parentComponent,
			IController controller, IView view) {

		setParentComponent(parentComponent);
		setController(controller);
		setView(view);

		init();
	}

	/**
	 * creates an Controller-Component
	 */
	protected void createComponent(IController controller) {
		setController(controller);

		init();
	}

	/**
	 * creates an Controller-Model-Component
	 * @param controller
	 * @param model
	 */
	protected void createComponent(IController controller, IModel model) {
		setController(controller);
		setModel(model);

		init();
	}

	/**
	 * creates an Controller-Model-View-Component
	 * @param controller
	 * @param model
	 * @param view
	 */
	protected void createComponent(IController controller, IModel model,
			IView view) {

		setController(controller);
		setModel(model);
		setView(view);

		init();
	}

	/**
	 * creates an Controller-View-Component
	 * @param controller
	 * @param view
	 */
	protected void createComponent(IController controller, IView view) {
		setController(controller);
		setView(view);

		init();
	}

	public IController getController() {
		return controller;
	}

	public List getListTerminatedChildComponents() {
		return listTerminatedChilds;
	}

	protected Map getMapEventListener() {
		return mapEventListener;
	}

	public IModel getModel() {
		return model;
	}

	public IComponent getParentComponent() {
		return parentComponent;
	}

	public IView getView() {
		return view;
	}

	/**
	 * conntect an initialize all component-parts
	 */
	private void init() {
		connectComponentParts();
		initTriadParts();
	}

	/**
	 * calls pre(), init() an post() on all component-parts
	 */
	private void initTriadParts() {

		/*
		 * call the pre() Method of every part
		 */
		if (model != null) {
			model.pre();
		}

		if (view != null) {
			view.pre();
		}

		if (controller != null) {
			controller.pre();
		}

		/*
		 * call the doInit()-Methode
		 */
		if (model != null) {
			model.init();
		}

		if (view != null) {
			view.init();
		}

		if (controller != null) {
			controller.init();
		}

		/*
		 * call the post()-Methode
		 */
		if (model != null) {
			model.post();
		}

		if (view != null) {
			view.post();
		}

		if (controller != null) {
			controller.post();
		}
		
		/*
		 * set the intialized status of the component-parts
		 */
		if (model != null) {
			model.setInitialized(true);
		}

		if (view != null) {
			view.setInitialized(true);
		}

		if (controller != null) {
			controller.setInitialized(true);
		}
		
	}

	public boolean isTerminated() {
		return terminated;
	}

	public void register(Class classOfEvent, IEventListener listener) {
		/*
		 * exists an List to store Listeners for the Event
		 */
		List listListener = null;
		if (mapEventListener.containsKey(classOfEvent)) {

			listListener = (List) mapEventListener.get(classOfEvent);

		} else {

			/*
			 * create new Vector to hold Listener for this Event TODO
			 * initialCapacity setzen
			 */
			listListener = new ArrayList();
			mapEventListener.put(classOfEvent, listListener);
		}

		listListener.add(listener);
	}

	public void removeListener(Class classOfEvent, IEventListener listener) {

		/*
		 * exists Listeners for this Event
		 */
		if (mapEventListener.containsKey(classOfEvent)) {

			/*
			 * remove Listener of this Event
			 */
			List listListeners = (List) mapEventListener.get(classOfEvent);
			listListeners.remove(listener);

			/*
			 * clean-up empty Lists
			 */
			if (listListeners.size() == 0) {
				mapEventListener.remove(classOfEvent);
			}
		}
	}

	public void setController(IController controller) {
		this.controller = controller;
	}

	public void setModel(IModel model) {
		this.model = model;
	}

	public void setParentComponent(IComponent parentComponent) {
		this.parentComponent = parentComponent;
	}

	public void setTerminated(boolean b) {
		this.terminated = b;
	}

	public void setView(IView view) {
		this.view = view;
	}

	public void terminate() {

		terminateChildComponents();
		removeTerminatedChildComponents();

		setTerminated(true);

		if (view != null) {
			view.setTerminated(true);
			view.terminate();
		}

		if (model != null) {
			model.setTerminated(true);
			model.terminate();
		}

		controller.setTerminated(true);
		controller.terminate();

		if (parentComponent != null) {
			parentComponent.addTerminatedChildComponent(this);
		}
	}

	/**
	 * calls terminate() on all child-components
	 */
	protected abstract void terminateChildComponents();
}
